package com.mydemo.springlearning.functions.access_limit.interceptor;

import com.mydemo.springlearning.functions.access_limit.annotation.AccessLimit;
import com.mydemo.springlearning.common.exception.ServiceException;
import com.mydemo.springlearning.common.utils.IpUtil;
import com.mydemo.springlearning.common.contant.Constant;
import com.mydemo.springlearning.common.entity.ResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
 * 接口防刷限流拦截器
 */
@Slf4j
public class AccessLimitInterceptor implements HandlerInterceptor {


    @Autowired
    RedisTemplate<String, String> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        AccessLimit annotation = method.getAnnotation(AccessLimit.class);
        if (annotation != null) {
            check(annotation, request);
        }

        return true;
    }

    private void check(AccessLimit annotation, HttpServletRequest request) {
        int maxCount = annotation.maxCount();
        int seconds = annotation.seconds();

        String key = Constant.Redis.ACCESS_LIMIT_PREFIX + request.getRequestURI();
        String ip = IpUtil.getIpAddr(request);
        key += ":" + ip;
        String o = redisTemplate.opsForValue().get(key);
        if (o == null) {
            // 第一次访问 （key，请求次数，ttl秒数）
            redisTemplate.opsForValue().set(key, String.valueOf(1), Duration.ofSeconds(seconds));
        } else {
            int count = 0;
            count = Integer.parseInt(o);
            //访问次数没有达到限制
            if (count < maxCount) {
                // 存活时间
                Long ttl = redisTemplate.getExpire(key, TimeUnit.SECONDS);
                // 限制key已经过期 计数置为1 （并发）
                if (ttl <= 0) {
                    redisTemplate.opsForValue().set(key, String.valueOf(1), Duration.ofSeconds(seconds));
                    //限制没有过期 计数加1
                } else {
                    redisTemplate.opsForValue().set(key, String.valueOf(++count), Duration.ofSeconds(ttl.intValue()));
                }
                //访问次数达到限制 直接不再处理请求
            } else {
                throw new ServiceException(ResponseCode.ACCESS_LIMIT.getMsg());
            }
        }
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    }
}
